package com.bravo.other.time;



import lombok.Getter;

import java.util.ArrayList;

/**
 * 我想要设计一个时间轮 来处理定时任务
 * 如果定时任务的延迟时间小于 tickDuration，则直接添加到时间轮的当前槽位中
 * 如果定时任务的延迟时间大于 tickDuration，则将任务添加到时间轮的指定槽位中
 * 如果定时任务的时间大小大于时间轮的容量，则绝执行任务 忽略
 * 单时间轮
 *
 */
public class TimingWheel {

    // 获取每个槽位的时长
    // 时间轮的基本属性
    @Getter
    private int tickDuration; // 每个槽位的时长
    // 获取槽位数量
    @Getter
    private int wheelSize;    // 时间轮大小(槽数量)
    // 获取当前时间
    @Getter
    private long currentTime; // 当前时间
    private int mask;         // 用于计算槽位的掩码
    
    // 时间轮的槽
    private TimerTaskList[] wheel;
    
    // 指针，指向当前所在的槽位
    private int position;
    
    // 构造函数
    public TimingWheel(int tickDuration, int wheelSize) {
        // TODO: 初始化时间轮
        this.tickDuration = tickDuration;
        this.wheelSize = wheelSize;
        this.wheel = new TimerTaskList[wheelSize];
        this.mask= wheelSize - 1;
        this.currentTime = System.currentTimeMillis();
        for (int i = 0; i <wheelSize; i++) {
            wheel[i] = new TimerTaskList();

        }
    }
    
    // 添加定时任务
    public boolean addTask(TimerTask task) {
        // TODO: 添加任务到时间轮
        if (task.getDelay() <= (long) tickDuration * wheelSize) {
            long expiration = currentTime + task.getDelay();
            int index = (int)(expiration / tickDuration);
            TimerTaskList bucket = wheel[index & mask];
            if (bucket == null) {
                bucket = new TimerTaskList();
                wheel[index & mask] = bucket;
            }
            bucket.add(task);
            return true;
        }

     // 不添加进时间轮组件
        return false;
    }

    /*
     *  1 处理推进时间  计算当前时间和 time的差值 计算需要向前推进多少个单位
     *  2 计算当前槽位
     *  3 处理到期任务
     *  4 处理时间轮的推进  修改position的值
     *
     */
    // 推进时间轮
    public void advanceClock(long time) {
        // TODO: 推进时间，处理到期任务
        if (time < currentTime) {
            throw new IllegalArgumentException("不能回退时间");
        }

        while (time-currentTime  >= tickDuration) {
            // 处理到期任务
            TimerTaskList bucket = wheel[position];
            bucket.expire();
            // 推进时间轮
            position = (position + 1) & mask;
            currentTime+= tickDuration;
        }
        // 报纸时间轮的精准 时间
        currentTime = time - (time % tickDuration);
    }

}

// 定时任务接口
interface TimerTask {
    void run();
    long getDelay();
}

// 定时任务列表(用于处理相同槽位的多个任务)
class TimerTaskList {

    private final java.util.List<TimerTask> list=new ArrayList<TimerTask>();

    public void add(TimerTask task) {
        // TODO: 添加任务
      list.add( task);
    }

    public void expire() {
        // TODO: 处理到期任务
        for (TimerTask task : list) {
            task.run();
        }
        list.clear();
    }
}
